/*
 * contrib/hstore/hstore_io.c
 */
#include "postgres.h"

#include <ctype.h>

#include "access/htup_details.h"
#include "catalog/pg_type.h"
#include "common/jsonapi.h"
#include "funcapi.h"
#include "hstore.h"
#include "lib/stringinfo.h"
#include "libpq/pqformat.h"
#include "parser/scansup.h"
#include "utils/builtins.h"
#include "utils/json.h"
#include "utils/jsonb.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"

PG_MODULE_MAGIC;

/* C函数的旧名称 */
HSTORE_POLLUTE(hstore_from_text, tconvert);


typedef struct
{
	char	   *begin;
	char	   *ptr;
	char	   *cur;
	char	   *word;
	int			wordlen;

	Pairs	   *pairs;
	int			pcur;
	int			plen;
} HSParser;

#define RESIZEPRSBUF \
do { \
		if ( fc_state->cur - fc_state->word + 1 >= fc_state->wordlen ) \
		{ \
				int32 fc_clen = fc_state->cur - fc_state->word; \
				fc_state->wordlen *= 2; \
				fc_state->word = (char*)repalloc( (void*)fc_state->word, fc_state->wordlen ); \
				fc_state->cur = fc_state->word + fc_clen; \
		} \
} while (0)


#define GV_WAITVAL 0
#define GV_INVAL 1
#define GV_INESCVAL 2
#define GV_WAITESCIN 3
#define GV_WAITESCESCIN 4

static bool fc_get_val(HSParser *fc_state, bool fc_ignoreeq, bool *fc_escaped)
{
	int			fc_st = GV_WAITVAL;

	fc_state->wordlen = 32;
	fc_state->cur = fc_state->word = palloc(fc_state->wordlen);
	*fc_escaped = false;

	while (1)
	{
		if (fc_st == GV_WAITVAL)
		{
			if (*(fc_state->ptr) == '"')
			{
				*fc_escaped = true;
				fc_st = GV_INESCVAL;
			}
			else if (*(fc_state->ptr) == '\0')
			{
				return false;
			}
			else if (*(fc_state->ptr) == '=' && !fc_ignoreeq)
			{
				elog(ERROR, "Syntax error near \"%.*s\" at position %d",
					 pg_mblen(fc_state->ptr), fc_state->ptr,
					 (int32) (fc_state->ptr - fc_state->begin));
			}
			else if (*(fc_state->ptr) == '\\')
			{
				fc_st = GV_WAITESCIN;
			}
			else if (!scanner_isspace((unsigned char) *(fc_state->ptr)))
			{
				*(fc_state->cur) = *(fc_state->ptr);
				fc_state->cur++;
				fc_st = GV_INVAL;
			}
		}
		else if (fc_st == GV_INVAL)
		{
			if (*(fc_state->ptr) == '\\')
			{
				fc_st = GV_WAITESCIN;
			}
			else if (*(fc_state->ptr) == '=' && !fc_ignoreeq)
			{
				fc_state->ptr--;
				return true;
			}
			else if (*(fc_state->ptr) == ',' && fc_ignoreeq)
			{
				fc_state->ptr--;
				return true;
			}
			else if (scanner_isspace((unsigned char) *(fc_state->ptr)))
			{
				return true;
			}
			else if (*(fc_state->ptr) == '\0')
			{
				fc_state->ptr--;
				return true;
			}
			else
			{
				RESIZEPRSBUF;
				*(fc_state->cur) = *(fc_state->ptr);
				fc_state->cur++;
			}
		}
		else if (fc_st == GV_INESCVAL)
		{
			if (*(fc_state->ptr) == '\\')
			{
				fc_st = GV_WAITESCESCIN;
			}
			else if (*(fc_state->ptr) == '"')
			{
				return true;
			}
			else if (*(fc_state->ptr) == '\0')
			{
				elog(ERROR, "Unexpected end of string");
			}
			else
			{
				RESIZEPRSBUF;
				*(fc_state->cur) = *(fc_state->ptr);
				fc_state->cur++;
			}
		}
		else if (fc_st == GV_WAITESCIN)
		{
			if (*(fc_state->ptr) == '\0')
				elog(ERROR, "Unexpected end of string");
			RESIZEPRSBUF;
			*(fc_state->cur) = *(fc_state->ptr);
			fc_state->cur++;
			fc_st = GV_INVAL;
		}
		else if (fc_st == GV_WAITESCESCIN)
		{
			if (*(fc_state->ptr) == '\0')
				elog(ERROR, "Unexpected end of string");
			RESIZEPRSBUF;
			*(fc_state->cur) = *(fc_state->ptr);
			fc_state->cur++;
			fc_st = GV_INESCVAL;
		}
		else
			elog(ERROR, "Unknown state %d at position line %d in file '%s'", fc_st, __LINE__, __FILE__);

		fc_state->ptr++;
	}
}

#define WKEY	0
#define WVAL	1
#define WEQ 2
#define WGT 3
#define WDEL	4


static void fc_parse_hstore(HSParser *fc_state)
{
	int			fc_st = WKEY;
	bool		fc_escaped = false;

	fc_state->plen = 16;
	fc_state->pairs = (Pairs *) palloc(sizeof(Pairs) * fc_state->plen);
	fc_state->pcur = 0;
	fc_state->ptr = fc_state->begin;
	fc_state->word = NULL;

	while (1)
	{
		if (fc_st == WKEY)
		{
			if (!fc_get_val(fc_state, false, &fc_escaped))
				return;
			if (fc_state->pcur >= fc_state->plen)
			{
				fc_state->plen *= 2;
				fc_state->pairs = (Pairs *) repalloc(fc_state->pairs, sizeof(Pairs) * fc_state->plen);
			}
			fc_state->pairs[fc_state->pcur].key = fc_state->word;
			fc_state->pairs[fc_state->pcur].keylen = hstoreCheckKeyLen(fc_state->cur - fc_state->word);
			fc_state->pairs[fc_state->pcur].val = NULL;
			fc_state->word = NULL;
			fc_st = WEQ;
		}
		else if (fc_st == WEQ)
		{
			if (*(fc_state->ptr) == '=')
			{
				fc_st = WGT;
			}
			else if (*(fc_state->ptr) == '\0')
			{
				elog(ERROR, "Unexpected end of string");
			}
			else if (!scanner_isspace((unsigned char) *(fc_state->ptr)))
			{
				elog(ERROR, "Syntax error near \"%.*s\" at position %d",
					 pg_mblen(fc_state->ptr), fc_state->ptr,
					 (int32) (fc_state->ptr - fc_state->begin));
			}
		}
		else if (fc_st == WGT)
		{
			if (*(fc_state->ptr) == '>')
			{
				fc_st = WVAL;
			}
			else if (*(fc_state->ptr) == '\0')
			{
				elog(ERROR, "Unexpected end of string");
			}
			else
			{
				elog(ERROR, "Syntax error near \"%.*s\" at position %d",
					 pg_mblen(fc_state->ptr), fc_state->ptr,
					 (int32) (fc_state->ptr - fc_state->begin));
			}
		}
		else if (fc_st == WVAL)
		{
			if (!fc_get_val(fc_state, true, &fc_escaped))
				elog(ERROR, "Unexpected end of string");
			fc_state->pairs[fc_state->pcur].val = fc_state->word;
			fc_state->pairs[fc_state->pcur].vallen = hstoreCheckValLen(fc_state->cur - fc_state->word);
			fc_state->pairs[fc_state->pcur].isnull = false;
			fc_state->pairs[fc_state->pcur].needfree = true;
			if (fc_state->cur - fc_state->word == 4 && !fc_escaped)
			{
				fc_state->word[4] = '\0';
				if (0 == pg_strcasecmp(fc_state->word, "null"))
					fc_state->pairs[fc_state->pcur].isnull = true;
			}
			fc_state->word = NULL;
			fc_state->pcur++;
			fc_st = WDEL;
		}
		else if (fc_st == WDEL)
		{
			if (*(fc_state->ptr) == ',')
			{
				fc_st = WKEY;
			}
			else if (*(fc_state->ptr) == '\0')
			{
				return;
			}
			else if (!scanner_isspace((unsigned char) *(fc_state->ptr)))
			{
				elog(ERROR, "Syntax error near \"%.*s\" at position %d",
					 pg_mblen(fc_state->ptr), fc_state->ptr,
					 (int32) (fc_state->ptr - fc_state->begin));
			}
		}
		else
			elog(ERROR, "Unknown state %d at line %d in file '%s'", fc_st, __LINE__, __FILE__);

		fc_state->ptr++;
	}
}

static int fc_comparePairs(const void *fc_a, const void *fc_b)
{
	const Pairs *fc_pa = fc_a;
	const Pairs *fc_pb = fc_b;

	if (fc_pa->keylen == fc_pb->keylen)
	{
		int			fc_res = memcmp(fc_pa->key, fc_pb->key, fc_pa->keylen);

		if (fc_res)
			return fc_res;

		/* 确保需要释放的内存会在后面处理 */
		if (fc_pb->needfree == fc_pa->needfree)
			return 0;
		else if (fc_pa->needfree)
			return 1;
		else
			return -1;
	}
	return (fc_pa->keylen > fc_pb->keylen) ? 1 : -1;
}

/*
 * 这段代码依然尊重pairs.needfree，尽管一般情况下
 * 它不应该在需要释放内存的上下文中被调用。
 * 我们保留它是因为（a）这些调用无论如何都在一个罕见的代码路径中，
 * （b）谁知道它们是否可能被某些调用者所需要。
 */
int hstoreUniquePairs(Pairs *fc_a, int32 fc_l, int32 *fc_buflen)
{
	Pairs	   *fc_ptr,
			   *fc_res;

	*fc_buflen = 0;
	if (fc_l < 2)
	{
		if (fc_l == 1)
			*fc_buflen = fc_a->keylen + ((fc_a->isnull) ? 0 : fc_a->vallen);
		return fc_l;
	}

	qsort((void *) fc_a, fc_l, sizeof(Pairs), fc_comparePairs);

	/*
	 * 我们不能在这里使用qunique，因为我们需要在
	 * 移除元素上运行一些清理代码。
	 */
	fc_ptr = fc_a + 1;
	fc_res = fc_a;
	while (fc_ptr - fc_a < fc_l)
	{
		if (fc_ptr->keylen == fc_res->keylen &&
			memcmp(fc_ptr->key, fc_res->key, fc_res->keylen) == 0)
		{
			if (fc_ptr->needfree)
			{
				pfree(fc_ptr->key);
				pfree(fc_ptr->val);
			}
		}
		else
		{
			*fc_buflen += fc_res->keylen + ((fc_res->isnull) ? 0 : fc_res->vallen);
			fc_res++;
			if (fc_res != fc_ptr)
				memcpy(fc_res, fc_ptr, sizeof(Pairs));
		}

		fc_ptr++;
	}

	*fc_buflen += fc_res->keylen + ((fc_res->isnull) ? 0 : fc_res->vallen);
	return fc_res + 1 - fc_a;
}

size_t hstoreCheckKeyLen(size_t fc_len)
{
	if (fc_len > HSTORE_MAX_KEY_LEN)
		ereport(ERROR,
				(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
				 errmsg("string too long for hstore key")));
	return fc_len;
}

size_t hstoreCheckValLen(size_t fc_len)
{
	if (fc_len > HSTORE_MAX_VALUE_LEN)
		ereport(ERROR,
				(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
				 errmsg("string too long for hstore value")));
	return fc_len;
}


HStore *
hstorePairs(Pairs *fc_pairs, int32 fc_pcount, int32 fc_buflen)
{
	HStore	   *fc_out;
	HEntry	   *fc_entry;
	char	   *fc_ptr;
	char	   *fc_buf;
	int32		fc_len;
	int32		fc_i;

	fc_len = CALCDATASIZE(fc_pcount, fc_buflen);
	fc_out = palloc(fc_len);
	SET_VARSIZE(fc_out, fc_len);
	HS_SETCOUNT(fc_out, fc_pcount);

	if (fc_pcount == 0)
		return fc_out;

	fc_entry = ARRPTR(fc_out);
	fc_buf = fc_ptr = STRPTR(fc_out);

	for (fc_i = 0; fc_i < fc_pcount; fc_i++)
		HS_ADDITEM(fc_entry, fc_buf, fc_ptr, fc_pairs[fc_i]);

	HS_FINALIZE(fc_out, fc_pcount, fc_buf, fc_ptr);

	return fc_out;
}


PG_FUNCTION_INFO_V1(hstore_in);
Datum hstore_in(PG_FUNCTION_ARGS)
{
	HSParser	fc_state;
	int32		fc_buflen;
	HStore	   *fc_out;

	fc_state.begin = PG_GETARG_CSTRING(0);

	fc_parse_hstore(&fc_state);

	fc_state.pcur = hstoreUniquePairs(fc_state.pairs, fc_state.pcur, &fc_buflen);

	fc_out = hstorePairs(fc_state.pairs, fc_state.pcur, fc_buflen);

	PG_RETURN_POINTER(fc_out);
}


PG_FUNCTION_INFO_V1(hstore_recv);
Datum hstore_recv(PG_FUNCTION_ARGS)
{
	int32		fc_buflen;
	HStore	   *fc_out;
	Pairs	   *fc_pairs;
	int32		fc_i;
	int32		fc_pcount;
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);

	fc_pcount = pq_getmsgint(fc_buf, 4);

	if (fc_pcount == 0)
	{
		fc_out = hstorePairs(NULL, 0, 0);
		PG_RETURN_POINTER(fc_out);
	}

	if (fc_pcount < 0 || fc_pcount > MaxAllocSize / sizeof(Pairs))
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
						fc_pcount, (int) (MaxAllocSize / sizeof(Pairs)))));
	fc_pairs = palloc(fc_pcount * sizeof(Pairs));

	for (fc_i = 0; fc_i < fc_pcount; ++fc_i)
	{
		int			fc_rawlen = pq_getmsgint(fc_buf, 4);
		int			fc_len;

		if (fc_rawlen < 0)
			ereport(ERROR,
					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
					 errmsg("null value not allowed for hstore key")));

		fc_pairs[fc_i].key = pq_getmsgtext(fc_buf, fc_rawlen, &fc_len);
		fc_pairs[fc_i].keylen = hstoreCheckKeyLen(fc_len);
		fc_pairs[fc_i].needfree = true;

		fc_rawlen = pq_getmsgint(fc_buf, 4);
		if (fc_rawlen < 0)
		{
			fc_pairs[fc_i].val = NULL;
			fc_pairs[fc_i].vallen = 0;
			fc_pairs[fc_i].isnull = true;
		}
		else
		{
			fc_pairs[fc_i].val = pq_getmsgtext(fc_buf, fc_rawlen, &fc_len);
			fc_pairs[fc_i].vallen = hstoreCheckValLen(fc_len);
			fc_pairs[fc_i].isnull = false;
		}
	}

	fc_pcount = hstoreUniquePairs(fc_pairs, fc_pcount, &fc_buflen);

	fc_out = hstorePairs(fc_pairs, fc_pcount, fc_buflen);

	PG_RETURN_POINTER(fc_out);
}


PG_FUNCTION_INFO_V1(hstore_from_text);
Datum hstore_from_text(PG_FUNCTION_ARGS)
{
	text	   *fc_key;
	text	   *fc_val = NULL;
	Pairs		fc_p;
	HStore	   *fc_out;

	if (PG_ARGISNULL(0))
		PG_RETURN_NULL();

	fc_p.needfree = false;
	fc_key = PG_GETARG_TEXT_PP(0);
	fc_p.key = VARDATA_ANY(fc_key);
	fc_p.keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(fc_key));

	if (PG_ARGISNULL(1))
	{
		fc_p.vallen = 0;
		fc_p.isnull = true;
	}
	else
	{
		fc_val = PG_GETARG_TEXT_PP(1);
		fc_p.val = VARDATA_ANY(fc_val);
		fc_p.vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(fc_val));
		fc_p.isnull = false;
	}

	fc_out = hstorePairs(&fc_p, 1, fc_p.keylen + fc_p.vallen);

	PG_RETURN_POINTER(fc_out);
}


PG_FUNCTION_INFO_V1(hstore_from_arrays);
Datum hstore_from_arrays(PG_FUNCTION_ARGS)
{
	int32		fc_buflen;
	HStore	   *fc_out;
	Pairs	   *fc_pairs;
	Datum	   *fc_key_datums;
	bool	   *fc_key_nulls;
	int			fc_key_count;
	Datum	   *fc_value_datums;
	bool	   *fc_value_nulls;
	int			fc_value_count;
	ArrayType  *fc_key_array;
	ArrayType  *fc_value_array;
	int			fc_i;

	if (PG_ARGISNULL(0))
		PG_RETURN_NULL();

	fc_key_array = PG_GETARG_ARRAYTYPE_P(0);

	Assert(ARR_ELEMTYPE(fc_key_array) == TEXTOID);

	/*
	 * 必须检查大于1，而不是不等于1，因为空数组的维度是0，
	 * 而不是1。
	 */

	if (ARR_NDIM(fc_key_array) > 1)
		ereport(ERROR,
				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
				 errmsg("wrong number of array subscripts")));

	deconstruct_array(fc_key_array,
					  TEXTOID, -1, false, TYPALIGN_INT,
					  &fc_key_datums, &fc_key_nulls, &fc_key_count);

	/* 参见hstoreArrayToPairs()中的讨论 */
	if (fc_key_count > MaxAllocSize / sizeof(Pairs))
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
						fc_key_count, (int) (MaxAllocSize / sizeof(Pairs)))));

	/* value_array可能为NULL */

	if (PG_ARGISNULL(1))
	{
		fc_value_array = NULL;
		fc_value_count = fc_key_count;
		fc_value_datums = NULL;
		fc_value_nulls = NULL;
	}
	else
	{
		fc_value_array = PG_GETARG_ARRAYTYPE_P(1);

		Assert(ARR_ELEMTYPE(fc_value_array) == TEXTOID);

		if (ARR_NDIM(fc_value_array) > 1)
			ereport(ERROR,
					(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
					 errmsg("wrong number of array subscripts")));

		if ((ARR_NDIM(fc_key_array) > 0 || ARR_NDIM(fc_value_array) > 0) &&
			(ARR_NDIM(fc_key_array) != ARR_NDIM(fc_value_array) ||
			 ARR_DIMS(fc_key_array)[0] != ARR_DIMS(fc_value_array)[0] ||
			 ARR_LBOUND(fc_key_array)[0] != ARR_LBOUND(fc_value_array)[0]))
			ereport(ERROR,
					(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
					 errmsg("arrays must have same bounds")));

		deconstruct_array(fc_value_array,
						  TEXTOID, -1, false, TYPALIGN_INT,
						  &fc_value_datums, &fc_value_nulls, &fc_value_count);

		Assert(fc_key_count == fc_value_count);
	}

	fc_pairs = palloc(fc_key_count * sizeof(Pairs));

	for (fc_i = 0; fc_i < fc_key_count; ++fc_i)
	{
		if (fc_key_nulls[fc_i])
			ereport(ERROR,
					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
					 errmsg("null value not allowed for hstore key")));

		if (!fc_value_nulls || fc_value_nulls[fc_i])
		{
			fc_pairs[fc_i].key = VARDATA(fc_key_datums[fc_i]);
			fc_pairs[fc_i].val = NULL;
			fc_pairs[fc_i].keylen =
				hstoreCheckKeyLen(VARSIZE(fc_key_datums[fc_i]) - VARHDRSZ);
			fc_pairs[fc_i].vallen = 4;
			fc_pairs[fc_i].isnull = true;
			fc_pairs[fc_i].needfree = false;
		}
		else
		{
			fc_pairs[fc_i].key = VARDATA(fc_key_datums[fc_i]);
			fc_pairs[fc_i].val = VARDATA(fc_value_datums[fc_i]);
			fc_pairs[fc_i].keylen =
				hstoreCheckKeyLen(VARSIZE(fc_key_datums[fc_i]) - VARHDRSZ);
			fc_pairs[fc_i].vallen =
				hstoreCheckValLen(VARSIZE(fc_value_datums[fc_i]) - VARHDRSZ);
			fc_pairs[fc_i].isnull = false;
			fc_pairs[fc_i].needfree = false;
		}
	}

	fc_key_count = hstoreUniquePairs(fc_pairs, fc_key_count, &fc_buflen);

	fc_out = hstorePairs(fc_pairs, fc_key_count, fc_buflen);

	PG_RETURN_POINTER(fc_out);
}


PG_FUNCTION_INFO_V1(hstore_from_array);
Datum hstore_from_array(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_in_array = PG_GETARG_ARRAYTYPE_P(0);
	int			fc_ndims = ARR_NDIM(fc_in_array);
	int			fc_count;
	int32		fc_buflen;
	HStore	   *fc_out;
	Pairs	   *fc_pairs;
	Datum	   *fc_in_datums;
	bool	   *fc_in_nulls;
	int			fc_in_count;
	int			fc_i;

	Assert(ARR_ELEMTYPE(fc_in_array) == TEXTOID);

	switch (fc_ndims)
	{
		case 0:
			fc_out = hstorePairs(NULL, 0, 0);
			PG_RETURN_POINTER(fc_out);

		case 1:
			if ((ARR_DIMS(fc_in_array)[0]) % 2)
				ereport(ERROR,
						(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
						 errmsg("array must have even number of elements")));
			break;

		case 2:
			if ((ARR_DIMS(fc_in_array)[1]) != 2)
				ereport(ERROR,
						(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
						 errmsg("array must have two columns")));
			break;

		default:
			ereport(ERROR,
					(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
					 errmsg("wrong number of array subscripts")));
	}

	deconstruct_array(fc_in_array,
					  TEXTOID, -1, false, TYPALIGN_INT,
					  &fc_in_datums, &fc_in_nulls, &fc_in_count);

	fc_count = fc_in_count / 2;

	/* 参见hstoreArrayToPairs()中的讨论 */
	if (fc_count > MaxAllocSize / sizeof(Pairs))
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
						fc_count, (int) (MaxAllocSize / sizeof(Pairs)))));

	fc_pairs = palloc(fc_count * sizeof(Pairs));

	for (fc_i = 0; fc_i < fc_count; ++fc_i)
	{
		if (fc_in_nulls[fc_i * 2])
			ereport(ERROR,
					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
					 errmsg("null value not allowed for hstore key")));

		if (fc_in_nulls[fc_i * 2 + 1])
		{
			fc_pairs[fc_i].key = VARDATA(fc_in_datums[fc_i * 2]);
			fc_pairs[fc_i].val = NULL;
			fc_pairs[fc_i].keylen =
				hstoreCheckKeyLen(VARSIZE(fc_in_datums[fc_i * 2]) - VARHDRSZ);
			fc_pairs[fc_i].vallen = 4;
			fc_pairs[fc_i].isnull = true;
			fc_pairs[fc_i].needfree = false;
		}
		else
		{
			fc_pairs[fc_i].key = VARDATA(fc_in_datums[fc_i * 2]);
			fc_pairs[fc_i].val = VARDATA(fc_in_datums[fc_i * 2 + 1]);
			fc_pairs[fc_i].keylen =
				hstoreCheckKeyLen(VARSIZE(fc_in_datums[fc_i * 2]) - VARHDRSZ);
			fc_pairs[fc_i].vallen =
				hstoreCheckValLen(VARSIZE(fc_in_datums[fc_i * 2 + 1]) - VARHDRSZ);
			fc_pairs[fc_i].isnull = false;
			fc_pairs[fc_i].needfree = false;
		}
	}

	fc_count = hstoreUniquePairs(fc_pairs, fc_count, &fc_buflen);

	fc_out = hstorePairs(fc_pairs, fc_count, fc_buflen);

	PG_RETURN_POINTER(fc_out);
}

/* hstore_from_record的大部分代码无耻地抄袭自record_out */

/*
 * 用于缓存记录I/O所需元数据的结构
 */
typedef struct ColumnIOData
{
	Oid			column_type;
	Oid			typiofunc;
	Oid			typioparam;
	FmgrInfo	proc;
} ColumnIOData;

typedef struct RecordIOData
{
	Oid			record_type;
	int32		record_typmod;
	/* 该字段仅在目标类型为复合类型的域时使用： */
	void	   *domain_info;	/* opaque cache for domain checks */
	int			ncolumns;
	ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER];
} RecordIOData;

PG_FUNCTION_INFO_V1(hstore_from_record);
Datum hstore_from_record(PG_FUNCTION_ARGS)
{
	HeapTupleHeader fc_rec;
	int32		fc_buflen;
	HStore	   *fc_out;
	Pairs	   *fc_pairs;
	Oid			fc_tupType;
	int32		fc_tupTypmod;
	TupleDesc	fc_tupdesc;
	HeapTupleData fc_tuple;
	RecordIOData *fc_my_extra;
	int			fc_ncolumns;
	int			fc_i,
				fc_j;
	Datum	   *fc_values;
	bool	   *fc_nulls;

	if (PG_ARGISNULL(0))
	{
		Oid			fc_argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);

		/*
		 * 我们没有元组可以查看，因此唯一的类型信息来源是
		 * argtype --- 它可能是复合类型的域，但我们在这里不关心，
		 * 因为我们不需要担心域约束。下面的lookup_rowtype_tupdesc_domain调用
		 * 如果在这里没有已知的复合类型oid，则会出错。
		 */
		fc_tupType = fc_argtype;
		fc_tupTypmod = -1;

		fc_rec = NULL;
	}
	else
	{
		fc_rec = PG_GETARG_HEAPTUPLEHEADER(0);

		/*
		 * 从元组本身提取类型信息 -- 即使对于匿名记录类型也有效。
		 */
		fc_tupType = HeapTupleHeaderGetTypeId(fc_rec);
		fc_tupTypmod = HeapTupleHeaderGetTypMod(fc_rec);
	}

	fc_tupdesc = lookup_rowtype_tupdesc_domain(fc_tupType, fc_tupTypmod, false);
	fc_ncolumns = fc_tupdesc->natts;

	/*
	 * 我们安排每系列调用只查找一次所需的I/O信息，
	 * 假设记录类型在我们下面不会改变。
	 */
	fc_my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
	if (fc_my_extra == NULL ||
		fc_my_extra->ncolumns != fc_ncolumns)
	{
		fcinfo->flinfo->fn_extra =
			MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
							   offsetof(RecordIOData, columns) +
							   fc_ncolumns * sizeof(ColumnIOData));
		fc_my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
		fc_my_extra->record_type = InvalidOid;
		fc_my_extra->record_typmod = 0;
	}

	if (fc_my_extra->record_type != fc_tupType ||
		fc_my_extra->record_typmod != fc_tupTypmod)
	{
		MemSet(fc_my_extra, 0,
			   offsetof(RecordIOData, columns) +
			   fc_ncolumns * sizeof(ColumnIOData));
		fc_my_extra->record_type = fc_tupType;
		fc_my_extra->record_typmod = fc_tupTypmod;
		fc_my_extra->ncolumns = fc_ncolumns;
	}

	Assert(fc_ncolumns <= MaxTupleAttributeNumber);	/* 因此，没有溢出 */
	fc_pairs = palloc(fc_ncolumns * sizeof(Pairs));

	if (fc_rec)
	{
		/* 构建一个临时 HeapTuple 控制结构 */
		fc_tuple.t_len = HeapTupleHeaderGetDatumLength(fc_rec);
		ItemPointerSetInvalid(&(fc_tuple.t_self));
		fc_tuple.t_tableOid = InvalidOid;
		fc_tuple.t_data = fc_rec;

		fc_values = (Datum *) palloc(fc_ncolumns * sizeof(Datum));
		fc_nulls = (bool *) palloc(fc_ncolumns * sizeof(bool));

		/* 将元组分解为字段 */
		heap_deform_tuple(&fc_tuple, fc_tupdesc, fc_values, fc_nulls);
	}
	else
	{
		fc_values = NULL;
		fc_nulls = NULL;
	}

	for (fc_i = 0, fc_j = 0; fc_i < fc_ncolumns; ++fc_i)
	{
		ColumnIOData *fc_column_info = &fc_my_extra->columns[fc_i];
		Form_pg_attribute fc_att = TupleDescAttr(fc_tupdesc, fc_i);
		Oid			fc_column_type = fc_att->atttypid;
		char	   *fc_value;

		/* 忽略数据类型中被丢弃的列 */
		if (fc_att->attisdropped)
			continue;

		fc_pairs[fc_j].key = NameStr(fc_att->attname);
		fc_pairs[fc_j].keylen = hstoreCheckKeyLen(strlen(NameStr(fc_att->attname)));

		if (!fc_nulls || fc_nulls[fc_i])
		{
			fc_pairs[fc_j].val = NULL;
			fc_pairs[fc_j].vallen = 4;
			fc_pairs[fc_j].isnull = true;
			fc_pairs[fc_j].needfree = false;
			++fc_j;
			continue;
		}

		/*
		 * 将列值转换为文本
		 */
		if (fc_column_info->column_type != fc_column_type)
		{
			bool		fc_typIsVarlena;

			getTypeOutputInfo(fc_column_type,
							  &fc_column_info->typiofunc,
							  &fc_typIsVarlena);
			fmgr_info_cxt(fc_column_info->typiofunc, &fc_column_info->proc,
						  fcinfo->flinfo->fn_mcxt);
			fc_column_info->column_type = fc_column_type;
		}

		fc_value = OutputFunctionCall(&fc_column_info->proc, fc_values[fc_i]);

		fc_pairs[fc_j].val = fc_value;
		fc_pairs[fc_j].vallen = hstoreCheckValLen(strlen(fc_value));
		fc_pairs[fc_j].isnull = false;
		fc_pairs[fc_j].needfree = false;
		++fc_j;
	}

	fc_ncolumns = hstoreUniquePairs(fc_pairs, fc_j, &fc_buflen);

	fc_out = hstorePairs(fc_pairs, fc_ncolumns, fc_buflen);

	ReleaseTupleDesc(fc_tupdesc);

	PG_RETURN_POINTER(fc_out);
}


PG_FUNCTION_INFO_V1(hstore_populate_record);
Datum hstore_populate_record(PG_FUNCTION_ARGS)
{
	Oid			fc_argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
	HStore	   *fc_hs;
	HEntry	   *fc_entries;
	char	   *fc_ptr;
	HeapTupleHeader fc_rec;
	Oid			fc_tupType;
	int32		fc_tupTypmod;
	TupleDesc	fc_tupdesc;
	HeapTupleData fc_tuple;
	HeapTuple	fc_rettuple;
	RecordIOData *fc_my_extra;
	int			fc_ncolumns;
	int			fc_i;
	Datum	   *fc_values;
	bool	   *fc_nulls;

	if (!type_is_rowtype(fc_argtype))
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg("first argument must be a rowtype")));

	if (PG_ARGISNULL(0))
	{
		if (PG_ARGISNULL(1))
			PG_RETURN_NULL();

		fc_rec = NULL;

		/*
		 * 我们没有元组可以查看，因此唯一的类型信息来源是
		 * argtype。下面的lookup_rowtype_tupdesc_domain调用将出错
		 * 如果在这里没有已知的复合类型oid。
		 */
		fc_tupType = fc_argtype;
		fc_tupTypmod = -1;
	}
	else
	{
		fc_rec = PG_GETARG_HEAPTUPLEHEADER(0);

		if (PG_ARGISNULL(1))
			PG_RETURN_POINTER(fc_rec);

		/*
		 * 从元组本身提取类型信息 -- 即使对于匿名记录类型也有效。
		 */
		fc_tupType = HeapTupleHeaderGetTypeId(fc_rec);
		fc_tupTypmod = HeapTupleHeaderGetTypMod(fc_rec);
	}

	fc_hs = PG_GETARG_HSTORE_P(1);
	fc_entries = ARRPTR(fc_hs);
	fc_ptr = STRPTR(fc_hs);

	/*
	 * 如果输入的hstore为空，只有在我们传入非空记录的情况下
	 * 才能跳过其余部分，否则可能会出现与域null相关的问题。
	 */

	if (HS_COUNT(fc_hs) == 0 && fc_rec)
		PG_RETURN_POINTER(fc_rec);

	/*
	 * 查找输入记录的tupdesc。此时，我们不担心
	 * 它是否是复合类型的域。
	 */
	fc_tupdesc = lookup_rowtype_tupdesc_domain(fc_tupType, fc_tupTypmod, false);
	fc_ncolumns = fc_tupdesc->natts;

	if (fc_rec)
	{
		/* 构建一个临时 HeapTuple 控制结构 */
		fc_tuple.t_len = HeapTupleHeaderGetDatumLength(fc_rec);
		ItemPointerSetInvalid(&(fc_tuple.t_self));
		fc_tuple.t_tableOid = InvalidOid;
		fc_tuple.t_data = fc_rec;
	}

	/*
	 * 我们安排每系列调用只查找一次所需的I/O信息，
	 * 假设记录类型在我们下面不会改变。
	 */
	fc_my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
	if (fc_my_extra == NULL ||
		fc_my_extra->ncolumns != fc_ncolumns)
	{
		fcinfo->flinfo->fn_extra =
			MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
							   offsetof(RecordIOData, columns) +
							   fc_ncolumns * sizeof(ColumnIOData));
		fc_my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
		fc_my_extra->record_type = InvalidOid;
		fc_my_extra->record_typmod = 0;
		fc_my_extra->domain_info = NULL;
	}

	if (fc_my_extra->record_type != fc_tupType ||
		fc_my_extra->record_typmod != fc_tupTypmod)
	{
		MemSet(fc_my_extra, 0,
			   offsetof(RecordIOData, columns) +
			   fc_ncolumns * sizeof(ColumnIOData));
		fc_my_extra->record_type = fc_tupType;
		fc_my_extra->record_typmod = fc_tupTypmod;
		fc_my_extra->ncolumns = fc_ncolumns;
	}

	fc_values = (Datum *) palloc(fc_ncolumns * sizeof(Datum));
	fc_nulls = (bool *) palloc(fc_ncolumns * sizeof(bool));

	if (fc_rec)
	{
		/* 将元组分解为字段 */
		heap_deform_tuple(&fc_tuple, fc_tupdesc, fc_values, fc_nulls);
	}
	else
	{
		for (fc_i = 0; fc_i < fc_ncolumns; ++fc_i)
		{
			fc_values[fc_i] = (Datum) 0;
			fc_nulls[fc_i] = true;
		}
	}

	for (fc_i = 0; fc_i < fc_ncolumns; ++fc_i)
	{
		ColumnIOData *fc_column_info = &fc_my_extra->columns[fc_i];
		Form_pg_attribute fc_att = TupleDescAttr(fc_tupdesc, fc_i);
		Oid			fc_column_type = fc_att->atttypid;
		char	   *fc_value;
		int			fc_idx;
		int			fc_vallen;

		/* 忽略数据类型中被丢弃的列 */
		if (fc_att->attisdropped)
		{
			fc_nulls[fc_i] = true;
			continue;
		}

		fc_idx = hstoreFindKey(fc_hs, 0,
							NameStr(fc_att->attname),
							strlen(NameStr(fc_att->attname)));

		/*
		 * 如果未找到键，我们不能直接跳过，因为我们可能需要处理一个域。
		 * 如果我们传递了一个非空记录数据，我们假设现有值是有效的
		 * （如果不是，那就不是我们的错），但如果我们传递了一个空值，
		 * 那么每个我们不填充的字段都需要通过输入函数处理，以防它是一个域类型。
		 */
		if (fc_idx < 0 && fc_rec)
			continue;

		/*
		 * 准备将列值从文本转换
		 */
		if (fc_column_info->column_type != fc_column_type)
		{
			getTypeInputInfo(fc_column_type,
							 &fc_column_info->typiofunc,
							 &fc_column_info->typioparam);
			fmgr_info_cxt(fc_column_info->typiofunc, &fc_column_info->proc,
						  fcinfo->flinfo->fn_mcxt);
			fc_column_info->column_type = fc_column_type;
		}

		if (fc_idx < 0 || HSTORE_VALISNULL(fc_entries, fc_idx))
		{
			/*
			 * 即使是空值也需要执行 InputFunctionCall，以便进行域
			 * 检查
			 */
			fc_values[fc_i] = InputFunctionCall(&fc_column_info->proc, NULL,
										  fc_column_info->typioparam,
										  fc_att->atttypmod);
			fc_nulls[fc_i] = true;
		}
		else
		{
			fc_vallen = HSTORE_VALLEN(fc_entries, fc_idx);
			fc_value = palloc(1 + fc_vallen);
			memcpy(fc_value, HSTORE_VAL(fc_entries, fc_ptr, fc_idx), fc_vallen);
			fc_value[fc_vallen] = 0;

			fc_values[fc_i] = InputFunctionCall(&fc_column_info->proc, fc_value,
										  fc_column_info->typioparam,
										  fc_att->atttypmod);
			fc_nulls[fc_i] = false;
		}
	}

	fc_rettuple = heap_form_tuple(fc_tupdesc, fc_values, fc_nulls);

	/*
	 * 如果目标类型是复合类型上的域，此时我们所知道的
	 * 是我们已经为基础复合类型制造了一个有效的值。
	 * 必须检查域约束后才能决定我们是否完成。
	 */
	if (fc_argtype != fc_tupdesc->tdtypeid)
		domain_check(HeapTupleGetDatum(fc_rettuple), false,
					 fc_argtype,
					 &fc_my_extra->domain_info,
					 fcinfo->flinfo->fn_mcxt);

	ReleaseTupleDesc(fc_tupdesc);

	PG_RETURN_DATUM(HeapTupleGetDatum(fc_rettuple));
}


static char * fc_cpw(char *fc_dst, char *fc_src, int fc_len)
{
	char	   *fc_ptr = fc_src;

	while (fc_ptr - fc_src < fc_len)
	{
		if (*fc_ptr == '"' || *fc_ptr == '\\')
			*fc_dst++ = '\\';
		*fc_dst++ = *fc_ptr++;
	}
	return fc_dst;
}

PG_FUNCTION_INFO_V1(hstore_out);
Datum hstore_out(PG_FUNCTION_ARGS)
{
	HStore	   *fc_in = PG_GETARG_HSTORE_P(0);
	int			fc_buflen,
				fc_i;
	int			fc_count = HS_COUNT(fc_in);
	char	   *fc_out,
			   *fc_ptr;
	char	   *fc_base = STRPTR(fc_in);
	HEntry	   *fc_entries = ARRPTR(fc_in);

	if (fc_count == 0)
		PG_RETURN_CSTRING(pstrdup(""));

	fc_buflen = 0;

	/*
	 * 这个循环由于对转义的悲观假设而高估了，
	 * 因此非常大的 hstore 值无法输出。这个问题可以解决，但
	 * 许多其他数据类型可能也有同样的问题。这个替换的代码
	 * 使用了原始的 varlena 大小进行计算，这在某些微妙之处是错误的。
	 */

	for (fc_i = 0; fc_i < fc_count; fc_i++)
	{
		/* 包含 "" 和 => 及逗号空格 */
		fc_buflen += 6 + 2 * HSTORE_KEYLEN(fc_entries, fc_i);
		/* 仅在非空时包含 "" */
		fc_buflen += 2 + (HSTORE_VALISNULL(fc_entries, fc_i)
					   ? 2
					   : 2 * HSTORE_VALLEN(fc_entries, fc_i));
	}

	fc_out = fc_ptr = palloc(fc_buflen);

	for (fc_i = 0; fc_i < fc_count; fc_i++)
	{
		*fc_ptr++ = '"';
		fc_ptr = fc_cpw(fc_ptr, HSTORE_KEY(fc_entries, fc_base, fc_i), HSTORE_KEYLEN(fc_entries, fc_i));
		*fc_ptr++ = '"';
		*fc_ptr++ = '=';
		*fc_ptr++ = '>';
		if (HSTORE_VALISNULL(fc_entries, fc_i))
		{
			*fc_ptr++ = 'N';
			*fc_ptr++ = 'U';
			*fc_ptr++ = 'L';
			*fc_ptr++ = 'L';
		}
		else
		{
			*fc_ptr++ = '"';
			fc_ptr = fc_cpw(fc_ptr, HSTORE_VAL(fc_entries, fc_base, fc_i), HSTORE_VALLEN(fc_entries, fc_i));
			*fc_ptr++ = '"';
		}

		if (fc_i + 1 != fc_count)
		{
			*fc_ptr++ = ',';
			*fc_ptr++ = ' ';
		}
	}
	*fc_ptr = '\0';

	PG_RETURN_CSTRING(fc_out);
}


PG_FUNCTION_INFO_V1(hstore_send);
Datum hstore_send(PG_FUNCTION_ARGS)
{
	HStore	   *fc_in = PG_GETARG_HSTORE_P(0);
	int			fc_i;
	int			fc_count = HS_COUNT(fc_in);
	char	   *fc_base = STRPTR(fc_in);
	HEntry	   *fc_entries = ARRPTR(fc_in);
	StringInfoData fc_buf;

	pq_begintypsend(&fc_buf);

	pq_sendint32(&fc_buf, fc_count);

	for (fc_i = 0; fc_i < fc_count; fc_i++)
	{
		int32		fc_keylen = HSTORE_KEYLEN(fc_entries, fc_i);

		pq_sendint32(&fc_buf, fc_keylen);
		pq_sendtext(&fc_buf, HSTORE_KEY(fc_entries, fc_base, fc_i), fc_keylen);
		if (HSTORE_VALISNULL(fc_entries, fc_i))
		{
			pq_sendint32(&fc_buf, -1);
		}
		else
		{
			int32		fc_vallen = HSTORE_VALLEN(fc_entries, fc_i);

			pq_sendint32(&fc_buf, fc_vallen);
			pq_sendtext(&fc_buf, HSTORE_VAL(fc_entries, fc_base, fc_i), fc_vallen);
		}
	}

	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}


/*
 * hstore_to_json_loose
 *
 * 这是一个启发式转换为 json 的操作，将
 * 't' 和 'f' 视为布尔值，并且看起来像数字的字符串视为数字，
 * 只要它们没有以零开头并跟着另一个数字
 * （想想以0开头的邮政编码或电话号码）。
 */
PG_FUNCTION_INFO_V1(hstore_to_json_loose);
Datum hstore_to_json_loose(PG_FUNCTION_ARGS)
{
	HStore	   *fc_in = PG_GETARG_HSTORE_P(0);
	int			fc_i;
	int			fc_count = HS_COUNT(fc_in);
	char	   *fc_base = STRPTR(fc_in);
	HEntry	   *fc_entries = ARRPTR(fc_in);
	StringInfoData fc_tmp,
				fc_dst;

	if (fc_count == 0)
		PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));

	initStringInfo(&fc_tmp);
	initStringInfo(&fc_dst);

	appendStringInfoChar(&fc_dst, '{');

	for (fc_i = 0; fc_i < fc_count; fc_i++)
	{
		resetStringInfo(&fc_tmp);
		appendBinaryStringInfo(&fc_tmp, HSTORE_KEY(fc_entries, fc_base, fc_i),
							   HSTORE_KEYLEN(fc_entries, fc_i));
		escape_json(&fc_dst, fc_tmp.data);
		appendStringInfoString(&fc_dst, ": ");
		if (HSTORE_VALISNULL(fc_entries, fc_i))
			appendStringInfoString(&fc_dst, "null");
		/* 猜测 't' 或 'f' 的值是布尔值 */
		else if (HSTORE_VALLEN(fc_entries, fc_i) == 1 &&
				 *(HSTORE_VAL(fc_entries, fc_base, fc_i)) == 't')
			appendStringInfoString(&fc_dst, "true");
		else if (HSTORE_VALLEN(fc_entries, fc_i) == 1 &&
				 *(HSTORE_VAL(fc_entries, fc_base, fc_i)) == 'f')
			appendStringInfoString(&fc_dst, "false");
		else
		{
			resetStringInfo(&fc_tmp);
			appendBinaryStringInfo(&fc_tmp, HSTORE_VAL(fc_entries, fc_base, fc_i),
								   HSTORE_VALLEN(fc_entries, fc_i));
			if (IsValidJsonNumber(fc_tmp.data, fc_tmp.len))
				appendBinaryStringInfo(&fc_dst, fc_tmp.data, fc_tmp.len);
			else
				escape_json(&fc_dst, fc_tmp.data);
		}

		if (fc_i + 1 != fc_count)
			appendStringInfoString(&fc_dst, ", ");
	}
	appendStringInfoChar(&fc_dst, '}');

	PG_RETURN_TEXT_P(cstring_to_text(fc_dst.data));
}

PG_FUNCTION_INFO_V1(hstore_to_json);
Datum hstore_to_json(PG_FUNCTION_ARGS)
{
	HStore	   *fc_in = PG_GETARG_HSTORE_P(0);
	int			fc_i;
	int			fc_count = HS_COUNT(fc_in);
	char	   *fc_base = STRPTR(fc_in);
	HEntry	   *fc_entries = ARRPTR(fc_in);
	StringInfoData fc_tmp,
				fc_dst;

	if (fc_count == 0)
		PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));

	initStringInfo(&fc_tmp);
	initStringInfo(&fc_dst);

	appendStringInfoChar(&fc_dst, '{');

	for (fc_i = 0; fc_i < fc_count; fc_i++)
	{
		resetStringInfo(&fc_tmp);
		appendBinaryStringInfo(&fc_tmp, HSTORE_KEY(fc_entries, fc_base, fc_i),
							   HSTORE_KEYLEN(fc_entries, fc_i));
		escape_json(&fc_dst, fc_tmp.data);
		appendStringInfoString(&fc_dst, ": ");
		if (HSTORE_VALISNULL(fc_entries, fc_i))
			appendStringInfoString(&fc_dst, "null");
		else
		{
			resetStringInfo(&fc_tmp);
			appendBinaryStringInfo(&fc_tmp, HSTORE_VAL(fc_entries, fc_base, fc_i),
								   HSTORE_VALLEN(fc_entries, fc_i));
			escape_json(&fc_dst, fc_tmp.data);
		}

		if (fc_i + 1 != fc_count)
			appendStringInfoString(&fc_dst, ", ");
	}
	appendStringInfoChar(&fc_dst, '}');

	PG_RETURN_TEXT_P(cstring_to_text(fc_dst.data));
}

PG_FUNCTION_INFO_V1(hstore_to_jsonb);
Datum hstore_to_jsonb(PG_FUNCTION_ARGS)
{
	HStore	   *fc_in = PG_GETARG_HSTORE_P(0);
	int			fc_i;
	int			fc_count = HS_COUNT(fc_in);
	char	   *fc_base = STRPTR(fc_in);
	HEntry	   *fc_entries = ARRPTR(fc_in);
	JsonbParseState *fc_state = NULL;
	JsonbValue *fc_res;

	(void) pushJsonbValue(&fc_state, WJB_BEGIN_OBJECT, NULL);

	for (fc_i = 0; fc_i < fc_count; fc_i++)
	{
		JsonbValue	fc_key,
					fc_val;

		fc_key.type = jbvString;
		fc_key.val.string.len = HSTORE_KEYLEN(fc_entries, fc_i);
		fc_key.val.string.val = HSTORE_KEY(fc_entries, fc_base, fc_i);

		(void) pushJsonbValue(&fc_state, WJB_KEY, &fc_key);

		if (HSTORE_VALISNULL(fc_entries, fc_i))
		{
			fc_val.type = jbvNull;
		}
		else
		{
			fc_val.type = jbvString;
			fc_val.val.string.len = HSTORE_VALLEN(fc_entries, fc_i);
			fc_val.val.string.val = HSTORE_VAL(fc_entries, fc_base, fc_i);
		}
		(void) pushJsonbValue(&fc_state, WJB_VALUE, &fc_val);
	}

	fc_res = pushJsonbValue(&fc_state, WJB_END_OBJECT, NULL);

	PG_RETURN_POINTER(JsonbValueToJsonb(fc_res));
}

PG_FUNCTION_INFO_V1(hstore_to_jsonb_loose);
Datum hstore_to_jsonb_loose(PG_FUNCTION_ARGS)
{
	HStore	   *fc_in = PG_GETARG_HSTORE_P(0);
	int			fc_i;
	int			fc_count = HS_COUNT(fc_in);
	char	   *fc_base = STRPTR(fc_in);
	HEntry	   *fc_entries = ARRPTR(fc_in);
	JsonbParseState *fc_state = NULL;
	JsonbValue *fc_res;
	StringInfoData fc_tmp;

	initStringInfo(&fc_tmp);

	(void) pushJsonbValue(&fc_state, WJB_BEGIN_OBJECT, NULL);

	for (fc_i = 0; fc_i < fc_count; fc_i++)
	{
		JsonbValue	fc_key,
					fc_val;

		fc_key.type = jbvString;
		fc_key.val.string.len = HSTORE_KEYLEN(fc_entries, fc_i);
		fc_key.val.string.val = HSTORE_KEY(fc_entries, fc_base, fc_i);

		(void) pushJsonbValue(&fc_state, WJB_KEY, &fc_key);

		if (HSTORE_VALISNULL(fc_entries, fc_i))
		{
			fc_val.type = jbvNull;
		}
		/* 猜测 't' 或 'f' 的值是布尔值 */
		else if (HSTORE_VALLEN(fc_entries, fc_i) == 1 &&
				 *(HSTORE_VAL(fc_entries, fc_base, fc_i)) == 't')
		{
			fc_val.type = jbvBool;
			fc_val.val.boolean = true;
		}
		else if (HSTORE_VALLEN(fc_entries, fc_i) == 1 &&
				 *(HSTORE_VAL(fc_entries, fc_base, fc_i)) == 'f')
		{
			fc_val.type = jbvBool;
			fc_val.val.boolean = false;
		}
		else
		{
			resetStringInfo(&fc_tmp);
			appendBinaryStringInfo(&fc_tmp, HSTORE_VAL(fc_entries, fc_base, fc_i),
								   HSTORE_VALLEN(fc_entries, fc_i));
			if (IsValidJsonNumber(fc_tmp.data, fc_tmp.len))
			{
				Datum		fc_numd;

				fc_val.type = jbvNumeric;
				fc_numd = DirectFunctionCall3(numeric_in,
										   CStringGetDatum(fc_tmp.data),
										   ObjectIdGetDatum(InvalidOid),
										   Int32GetDatum(-1));
				fc_val.val.numeric = DatumGetNumeric(fc_numd);
			}
			else
			{
				fc_val.type = jbvString;
				fc_val.val.string.len = HSTORE_VALLEN(fc_entries, fc_i);
				fc_val.val.string.val = HSTORE_VAL(fc_entries, fc_base, fc_i);
			}
		}
		(void) pushJsonbValue(&fc_state, WJB_VALUE, &fc_val);
	}

	fc_res = pushJsonbValue(&fc_state, WJB_END_OBJECT, NULL);

	PG_RETURN_POINTER(JsonbValueToJsonb(fc_res));
}
